﻿using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using GraphSharp.Controls;

namespace XmlStudio.Controls {
    /// <summary>
    /// Represents the object which when placed on other object it can cause the change of its size.
    /// </summary>
    public class ResizeThumb : Thumb {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the ResizeThumb.
        /// </summary>
        public ResizeThumb() {
            // add the event handler for the event DragDelta
            DragDelta += ResizeThumb_DragDelta;
        }
        #endregion

        #region Event handlers
        /// <summary>
        /// DragDelta event handler.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e) {
            var item = this.DataContext as VertexControl;

            if(item != null && item.IsSelected) {
                double minLeft, minTop, minDeltaHorizontal, minDeltaVertical;
                double dragDeltaVertical, dragDeltaHorizontal, scale;

                CalculateDragLimits(item, out minLeft, out minTop, out minDeltaHorizontal, out minDeltaVertical);

                switch(VerticalAlignment) {
                    case VerticalAlignment.Bottom:
                        dragDeltaVertical = Math.Min(-e.VerticalChange, minDeltaVertical);
                        scale = (item.ActualHeight - dragDeltaVertical) / item.ActualHeight;
                        DragBottom(scale, item);
                        break;
                    case VerticalAlignment.Top:
                        dragDeltaVertical = Math.Min(Math.Max(-minTop, e.VerticalChange), minDeltaVertical);
                        scale = (item.ActualHeight - dragDeltaVertical) / item.ActualHeight;
                        DragTop(scale, item);
                        break;
                    default:
                        break;
                }

                switch(HorizontalAlignment) {
                    case HorizontalAlignment.Left:
                        dragDeltaHorizontal = Math.Min(Math.Max(-minLeft, e.HorizontalChange), minDeltaHorizontal);
                        scale = (item.ActualWidth - dragDeltaHorizontal) / item.ActualWidth;
                        DragLeft(scale, item);
                        break;
                    case HorizontalAlignment.Right:
                        dragDeltaHorizontal = Math.Min(-e.HorizontalChange, minDeltaHorizontal);
                        scale = (item.ActualWidth - dragDeltaHorizontal) / item.ActualWidth;
                        DragRight(scale, item);
                        break;
                    default:
                        break;
                }

                e.Handled = true;
            }
        }
        #endregion

        #region Helper methods
        public static double GetX(DependencyObject obj) {
            return (double)obj.GetValue(DragBehaviour.XProperty);
        }

        public static void SetX(DependencyObject obj, double value) {
            obj.SetValue(DragBehaviour.XProperty, value);
        }

        public static double GetY(DependencyObject obj) {
            return (double)obj.GetValue(DragBehaviour.YProperty);
        }

        public static void SetY(DependencyObject obj, double value) {
            obj.SetValue(DragBehaviour.YProperty, value);
        }

        private static void DragLeft(double scale, VertexControl item) {
            double itemX = GetX(item);
            double delta = item.ActualWidth * (scale - 1);
            SetX(item, itemX - delta / 2);
            item.Width = item.ActualWidth + delta;
        }

        private static void DragTop(double scale, VertexControl item) {
            double groupItemTop = GetY(item);
            double delta = item.ActualHeight * (scale - 1);
            SetY(item, groupItemTop - delta / 2);
            item.Height = item.ActualHeight * scale;
        }

        private static void DragRight(double scale, VertexControl item) {
            double delta = item.ActualWidth * (scale - 1);
            var itemX = GetX(item);
            item.Width = item.ActualWidth + delta;
            SetX(item, itemX + delta / 2);
        }

        private static void DragBottom(double scale, VertexControl item) {
            double groupItemTop = GetY(item);
            double delta = (item.ActualHeight) * (scale - 1);
            SetY(item, groupItemTop + delta / 2);
            item.Height = item.ActualHeight + delta;
        }

        private static void CalculateDragLimits(VertexControl selectedItem, out double minLeft, out double minTop, out double minDeltaHorizontal, out double minDeltaVertical) {
            minLeft = double.MaxValue;
            minTop = double.MaxValue;
            minDeltaHorizontal = double.MaxValue;
            minDeltaVertical = double.MaxValue;

            //// drag limits are set by these parameters: canvas top, canvas left, minHeight, minWidth
            //// calculate min value for each parameter for each item
            double left = GetX(selectedItem);
            double top = GetY(selectedItem);

            minLeft = double.IsNaN(left) ? 0 : Math.Min(left, minLeft);
            minTop = double.IsNaN(top) ? 0 : Math.Min(top, minTop);

            minDeltaVertical = Math.Min(minDeltaVertical, selectedItem.ActualHeight - selectedItem.MinHeight);
            minDeltaHorizontal = Math.Min(minDeltaHorizontal, selectedItem.ActualWidth - selectedItem.MinWidth);
        }
        #endregion
    }
}